﻿using System;
using System.Diagnostics;
using System.Threading;
using AzureStorageMapper.TableStorage;
using AzureStorageMapper.TableStorage.Attributes;
using NUnit.Framework;
using AzureStorageMapper.TableStorage.ActionModes;

namespace AzureStorageMapper.Tests.TableStorage.TableContextTests
{
    public class CURDAsyncTests
    {
        const string DefaultPartitionKey = "default";
        TableContext<SampleTableModel> tc = new TableContext<SampleTableModel>();
        bool isRunning = true;
        SampleTableModel modelInit;

        [SetUp]
        public void SetUpTest()
        {
            Trace.TraceInformation("Creatinig Table ...");
            tc.CreateTable();

            Trace.TraceInformation("Sleep for 10 seconds ...");
            Thread.Sleep(TimeSpan.FromSeconds(10));

            modelInit = new SampleTableModel(DefaultPartitionKey, Guid.NewGuid().ToString());
            modelInit.ColumnOne = "col 1";
            modelInit.ColumnTwo = 1;
            modelInit.ColumnThree = 1L;
        }

        [TearDown]
        public void TearDownTest()
        {
            Trace.TraceInformation("Sleep for 10 seconds before deleting table ...");
            Thread.Sleep(TimeSpan.FromSeconds(10));

            Trace.TraceInformation("Deleting Table ...");
            tc.DeleteTable();
        }

        [Test]
        public void CURDAsyncTest()
        {
            Trace.TraceInformation("Saving data ...");
            tc.BeginSave(modelInit, callback:SaveCallBack);

            while (isRunning)
            {
                Trace.TraceInformation("Waiting for test to finish .....");
                Thread.Sleep(1000);
            }
        }

        private void SaveCallBack(IAsyncResult result)
        {
            Trace.TraceInformation("Save: '{0}'", result.IsCompleted);
            Assert.IsTrue(result.IsCompleted);

            Trace.TraceInformation("Retrive and Compare result ...");
            SampleTableModel model = tc.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(modelInit.ColumnOne, model.ColumnOne);
            Assert.AreEqual(modelInit.ColumnTwo, model.ColumnTwo);
            Assert.AreEqual(modelInit.ColumnThree, model.ColumnThree);
            tc.EndSave(result);

            Thread.Sleep(3000);

            modelInit.ColumnOne = model.ColumnOne + modelInit.ColumnOne;
            tc.BeginUpdate(modelInit, callback: UpdateCallback);
        }

        private void UpdateCallback(IAsyncResult result)
        {
            Trace.TraceInformation("Update: '{0}'", result.IsCompleted);
            Assert.IsTrue(result.IsCompleted);

            Trace.TraceInformation("Retrive and Compare result ...");
            SampleTableModel model = tc.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Assert.AreEqual(modelInit.ColumnOne, model.ColumnOne);
            Assert.AreEqual(modelInit.ColumnTwo, model.ColumnTwo);
            Assert.AreEqual(modelInit.ColumnThree, model.ColumnThree);
            tc.EndUpdate(result);

            Thread.Sleep(3000);

            tc.BeginDelete(modelInit, DeleteCallback);
        }

        private void DeleteCallback(IAsyncResult result)
        {
            Trace.TraceInformation("Delete: '{0}'", result.IsCompleted);
            Assert.IsTrue(result.IsCompleted);
            tc.EndDelete(result);

            Trace.TraceInformation("Retrive deleted data ...");
            SampleTableModel model = tc.FindEntity(modelInit.PartitionKey, modelInit.RowKey);
            Trace.TraceInformation("Retrive deleted data is null : '{0}'...", model == null);
            Assert.IsNull(model);
            
            Thread.Sleep(3000);

            isRunning = false;
        }

        [TableModel(Name = "CURDAsyncTest")]
        internal class SampleTableModel : TableEntity
        {
            [TableColumn]
            public string ColumnOne { get; set; }

            [TableColumn(Name = "Column2")]
            public int ColumnTwo { get; set; }

            [TableColumn]
            public long ColumnThree { get; set; }

            public SampleTableModel(string partitionKey, string rowKey)
                : base(partitionKey, rowKey)
            {
            }
        }

    }
}
